package core;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import core.reports.IHost;
import core.reports.IVisitor;
import core.reports.TraversalOrder;


/** This class is a singleton design.  It contains storage units and the products
 * 
 * 
 */
public class RootStorageUnit extends ModelObject implements IHost
{    
	/**Id used for serialization
	 * 
	 */
	private static final long serialVersionUID = 3310106453004626629L;
	
	/** This is the one instance that should be created of this class. 
	 */
    private static RootStorageUnit instance;
    /** The constructor of this class should never be called.  To get object call
     *  getInstance().
     */
    private List <StorageUnit> storageUnits;
    
    private static int nextID = 0;
    private String prevID;
    
    public int generateID()
    {
    	if(prevID != null)
    	{
    		nextID = Integer.parseInt(prevID) + 1;
    	}
    	prevID = Integer.toString(nextID);
    	
    	return nextID;
    }
    
    
    public List<ProductContainer> getAllProductContainers()
    {
    	List<ProductContainer> list = new ArrayList<ProductContainer>();
    	for(ProductContainer su : storageUnits)
    	{
    		list.add(su);
    		list.addAll(getAllChildrenRecursive(su));
    	}
    	return list;
    }
    
    private List<ProductContainer> getAllChildrenRecursive(ProductContainer pc)
    {
    	List<ProductContainer> list = new ArrayList<ProductContainer>();
    	for(ProductContainer pg : pc.groups)
    	{
    		list.add(pg);
    		list.addAll(getAllChildrenRecursive(pg));
    	}
    	return list;
    	
    }
    
    public ProductContainer getProductContainer(int id)
    {
    	List<ProductContainer> list = getAllProductContainers();
    	for(ProductContainer cont : list)
    	{
    		if(cont.getID() == id)
    			return cont;
    	}
    	
    	return null;
    }
    
    /** 
     * first time called this function creates an object and returns it.
     * Subsequent calls return the object.  This maintains the Singleton design.
    */
    public static RootStorageUnit getInstance()
    {
        if(instance == null)
        {
            instance = new RootStorageUnit();
        }
        return instance;
    }
    
    public static void destroyInstance()
    {
    	instance = null;
    }
    
    public static void removeInstance()
    {
    	instance = null;
    }
    
    //Singleton design pattern
    /** Constructor.  Set to private to control the number of instances. 
     */
    private RootStorageUnit()
    {
    	storageUnits = new ArrayList<StorageUnit>();
    }
    
    
    /** Creates a new storage unit and adds it to the tracker. 
     * @param nameOfUnit the name of the new storage unit added.
     * @return the new storage unit just added
     * @throws IllegalArgumentException when if adding fails
     */
    public StorageUnit addStorageUnit(StorageUnit su)throws IllegalArgumentException
    {
    	assert(su != null);
    	assert(canAddStorageUnit(su));
    	if(!canAddStorageUnit(su))
    		throw new IllegalArgumentException("Not a valid Storage Unit name");
    	
    	storageUnits.add(su);
    	return su;
    }
    
    /** Creates a new storage unit and adds it to the tracker. 
     * @param nameOfUnit the name of the new storage unit added.
     * @return the new storage unit just added
     * @throws IllegalArgumentException when if adding fails
     */
    public StorageUnit addStorageUnit(StorageUnit su, int ID)throws IllegalArgumentException
    {
    	assert(su != null);
    	assert(canAddStorageUnit(su));
    	su.setPCID(ID);
    	su.setID(ID);
    	if(!canAddStorageUnit(su))
    		throw new IllegalArgumentException("Not a valid Storage Unit name");
    	
    	storageUnits.add(su);
    	return su;
    }
    
    /** UI calls this function to give user action feedback. 
     * Show if a storage unit can be added.
     * @param su sees if the StorageUnit can be added
     * @return returns true if su can be added
     */
    public boolean canAddStorageUnit(StorageUnit su)
    {
    	assert(su != null);
        //assert(!su.getName().isEmpty());
        //assert(!storageUnits.contains(su));
        if(su.getName().isEmpty() || storageUnits.contains(su))
        	return false;
        return true;
    }
    
    //storage unit sends itself up to parent to be deleted
    /** When a storage unit is removed, it call it's parent to delete it.
     * @param storageUnit the storage unit to be removed.
     * @return the storage unit that was removed, or null
     * @throws Exception if fails to remove storage unit
     */
    public boolean deleteStorageUnit(StorageUnit storageUnit)throws Exception
    {
    	assert(storageUnit != null);
    	storageUnits.remove(storageUnit);
        return true;
    }
    
    /** UI calls this function to give user action feedback.  
     * Shows if it can remove a storage unit 
     * @param storageUnit the storage unit in question to remove.
     * @return true if can remove storage unit
     * */
    public boolean canDeleteStorageUnit(ProductContainer storageUnit)
    {
        // This will be done in the facade... RootStorageUnit won't have access 
    	//  the item and product manager so facade will do this... we don't need this?
        return false;
    }
    
    /** Modifies a storage unit
     * 
     * @param before the su to be replaced
     * @param after the su that replaces
     * @return after if successful
     * @throws IllegalArgumentException
     * @throws Exception if modify failed
     */
    public StorageUnit editStorageUnit(String before, String after) 
    		throws IllegalArgumentException, Exception
	{
    	assert(!before.isEmpty() && !after.isEmpty());
    	StorageUnit toCheck = new StorageUnit(after);
    	if(canAddStorageUnit(toCheck)) // check for non empty and uniqueness
    	{
    		StorageUnit su = getStorageUnit(before);
    		if(su==null)
    			throw new IllegalArgumentException("Storage Unit does not exist");
    		else
    		{
    			su.setName(after);
    			return su;
    		}
    	}
		throw new IllegalArgumentException("Invalid storage unit name");
	}
    
    /**
     * Gets the number of storage units in the system
     * @return number of storage units
     */
    public int getStorageUnitCount()
    {
    	return storageUnits.size();
    }
    
    /**
     * finds the storage unit in the list and returns it
     * @param suName name of the storage unit we are looking for
     * @return null if it is not found, or the storage unit
     */
    public StorageUnit getStorageUnit(String suName)
    {
    	assert(!suName.isEmpty());
    	
    	StorageUnit unit = new StorageUnit(suName);
    	int index = storageUnits.indexOf(unit);
    	if(index < 0)
    		return null;
    	return storageUnits.get(index);
    }
    
    /**
     * This returns the List of StorageUnits
     * 
     */
    public List<StorageUnit> getStorageUnits()
    {
    	return storageUnits;
    }
    
    @Override
    public void accept(IVisitor v, TraversalOrder order)
    {
    	//Since this node never gets visited then it doesn't
    	//matter when the children get visited.  So just 
    	//visit all of the children
    	for (int i = 0; i < storageUnits.size(); i++)
    	{
    		storageUnits.get(i).accept(v, order);
    	}
    }
}
